package com.terren.spider.util.common;


import com.google.common.base.Charsets;

import java.util.zip.Checksum;

/** FNV-1a (http://www.isthe.com/chongo/tech/comp/fnv). */
class FNV64 implements Checksum {
    private  final static long OFFSET = 0xcbf29ce484222325L;
    private  final static long PRIME = 1099511628211L;

    private long hash = OFFSET;

    @Override
    public void update(int b) {
        hash ^= 0xffL & b;
        hash *= PRIME;
    }

    @Override
    public void update(byte[] b, int off, int len) {
        int stop = off + len;
        for (int i = off; i < stop; i++) {
            hash ^= 0xffL & b[i];
            hash *= PRIME;
        }
    }

    @Override
    /** Note: Java's long is signed. */
    public long getValue() {
        return hash;
    }

    @Override
    public void reset() {
        hash = OFFSET;
    }
}

public class FNV {
    private static byte[] intToByte(int value) {
        return new byte[]{
                (byte) (value >>> 24),
                (byte) (value >>> 16),
                (byte) (value >>> 8),
                (byte) value};
    }

    private static byte[] longToByte(long value) {
        return new byte[]{
                (byte) (value >>> 56),
                (byte) (value >>> 48),
                (byte) (value >>> 40),
                (byte) (value >>> 32),
                (byte) (value >>> 24),
                (byte) (value >>> 16),
                (byte) (value >>> 8),
                (byte) value};
    }

    public static long hash(Object ...values) {
        FNV64 f = new FNV64();
        for (Object v: values) {
            byte[] buf = null;

            if (v instanceof Integer) {
                int i = (Integer) v;
                buf = intToByte(i);
            } else if (v instanceof Long) {
                long value = (Long) v;
                if ( (value & 0xFFFFFFFF00000000L) != 0) {
                    buf = longToByte(value);
                } else {
                    buf = intToByte((int) value);
                }
            } else if (v instanceof  String) {
                buf = ((String) v).getBytes(Charsets.UTF_8);
            } else if ( v instanceof byte[]) {
                buf = (byte[]) v;
            } else  {
                // 不使用hashCode, 只支持4种基本类型
                // int h = v.hashCode();
                // f.update(h);
                throw new IllegalArgumentException("not supported type");
            }
            f.update(buf, 0, buf.length);
        }
        return f.getValue();
    }

    public static String hashHex(Object ...values) {
        long v = hash(values);
        return Long.toHexString(v);
    }
}